﻿using System;
using System.IO;

namespace Azure.Online.Proxy.Server {
    public class HttpServerChunkedRequestStream : HttpServerRequestStream {

        private int _position;
        private byte[] _subchunk;
        private int _chunkOffset;
        private int _chunkSize;
        private int _subchunkOffset;

        public HttpServerChunkedRequestStream(HttpServerContext context, Stream inner)
            : base(context, inner, 0) {
        }

        public override int Read(byte[] buffer, int offset, int count) {
            EnsureChunk();
            var loadedRest = _subchunk.Length - _subchunkOffset;
            var read = loadedRest < count ? loadedRest : count;
            for (var i = 0; i < read; i++) {
                buffer[offset] = _subchunk[_subchunkOffset];
                offset++;
                _subchunkOffset++;
                _chunkOffset++;
            }
            _position += read;
            return read;
        }

        public override long Position {
            get { return _position; }
            set { throw new InvalidOperationException(); }
        }

        private void EnsureChunk() {
            if (_subchunk == null) {
                _subchunk = new byte[2048];
            }

            if (_chunkOffset >= _chunkSize) {
                _chunkSize = ReadChunkLength();
                _subchunkOffset = 0;
                _chunkOffset = 0;

                if (_chunkSize == 0) {
                    _inner.Read(_subchunk, 0, 4); //TODO: hard coded to 13, 10, 13, 10. But there may be http headers
                } else {
                    var loadSize = _chunkSize < _subchunk.Length ? _chunkSize : _subchunk.Length;
                    _inner.Read(_subchunk, 0, loadSize);
                    _inner.Read(_subchunk, 0, 2);
                }

            } else if (_subchunkOffset >= _subchunk.Length) {
                var chunkRest = _chunkSize - _chunkOffset;
                _subchunkOffset = 0;

                var loadSize = chunkRest < _subchunk.Length ? chunkRest : _subchunk.Length;
                _inner.Read(_subchunk, 0, loadSize);
            }
        }

        private int ReadChunkLength() {
            var res = 0;
            do {
                byte bt = (byte)_inner.ReadByte();
                if (bt >= '0' && bt <= '9') {
                    res = res * 16 + (bt - 48);
                } else if (bt >= 'A' && bt <= 'F') {
                    res = res * 16 + (bt - (byte)'A');
                } else if (bt >= 'a' && bt <= 'f') {
                    res = res * 16 + (bt - (byte)'a');
                } else {
                    break;
                }
            } while (true);
            ReadByte();
            return res;
        }
    }
}
